
# Copyright 2009 David Lee Miller
#
# This file is part of POSSM (Python Open Source Scanning Microscopy).
#
# POSSM is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# POSSM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License         
# along with POSSM.  If not, see <http://www.gnu.org/licenses/>.
#                                                                           
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

                              #_ _   _____       _             __
                             #| (_) |_   _|     | |           / _|
  #___ ___  _ __ ___   ___  __| |_    | |  _ __ | |_ ___ _ __| |_ __ _  ___ ___
 #/ __/ _ \| '_ ` _ \ / _ \/ _` | |   | | | '_ \| __/ _ \ '__|  _/ _` |/ __/ _ \
#| (_| (_) | | | | | |  __/ (_| | |  _| |_| | | | ||  __/ |  | || (_| | (_|  __/
 #\___\___/|_| |_| |_|\___|\__,_|_| |_____|_| |_|\__\___|_|  |_| \__,_|\___\___|
#################################################################################

#This file defines the read a write funtions for the
#NI-6030/pci-mio-16xe-10 Card using the Comedi library. DLM

from controller import n,os,struct
import random as ra



class Device(object):
	'''
	A class for basic comedi recognition of devices and subdevices. Also collects hardware
	specific attributes of the device.
	
	Arguments:
	dev_no -- the comedi device number
	
	Optional Arguments:
	dig -- set as False if device has no digital IO capabilities
	ai -- set as False if device has no analog input capabilities
	ao -- set as False if device has no analog output capabilities
	fd -- set as True if using device with hardware buffer capabilities
	'''
	
	DEV_NO = None
	DEV = None
	FD = None
	DEV_FD = None
	SUB_DIO = None
	SUB_AI = None
	SUB_AO = None
	AO_BITS = None
	AI_BITS = None
	
	def __init__(self,dev_no,dig=True,ai=True,ao=True,fd=False):
		'''
		Initialzation of devices. dig, ai and ao are all set to
		True. If the board does not have one of these devices, mark
		it false.
		
		'''
		
		self.DEV_NO = dev_no
		self.PATH = '/dev/comedi'+str(self.DEV_NO)
		
		self.FindDevice()
		if fd and self.DEV: self.OpenFileDescriptor()
		if self.DEV:
			self.FindSubdevices(dig,ai,ao)
			self.ApplyCalibration()
			self.GetBits()
	
	def FindDevice(self):
		'''
		Locates the device from device file.
		
		'''
		self.DEV = True
	
	def OpenFileDescriptor(self):
		'''
		Opens a file descriptor for use with devices
		that have on board clocks. Required for block read/write
		commands that have a data buffer.
		
		'''
		self.FD = True
	
	def FindSubdevices(self,dig,ai,ao):
		'''
		Finds the needed subdevices for the device board.
		
		'''
		if dig:
			self.SUB_DIO = True
		else: pass
		
		if ai:
			self.SUB_AI = True
		else: pass
		
		if ao:
			self.SUB_AO = True
		else: pass
	
	def ApplyCalibration(self):
		'''
		Calibrates the analog subdevices on the board. Must be used in
		conjunction with the external comedi_calibrate function.
		
		'''
		pass
	
	def GetBits(self):
		'''
		Finds the number of bits on the board for the various subdevices.
		
		'''
		if self.SUB_AI != None:
			self.AI_MAXDATA = 65535
			num = 2
			bits = 0
			while num < self.AI_MAXDATA:
				bits += 1
				num = 2**bits
			self.AI_BITS = bits
		if self.SUB_AO != None:
			self.AO_MAXDATA = 65535
			num = 2
			bits = 0
			while num < self.AO_MAXDATA:
				bits += 1
				num = 2**bits
			self.AO_BITS = bits

class DeviceFunctions(Device):
	'Low level class for basic device functionality.'
	
	DIO_OUT = True
	DIO_IN = True
	#AI_BITS = 16	#for other bit resolutions, change upon instantiation of the class
	#AO_BITS = 16
	
	buf_size = 4096
	extralen = n.add(n.zeros((buf_size)),1)
	freq = 200000.0
	
	def __init__(self,dev_no,dig=True,ai=True,ao=True,fd=False):
		Device.__init__(self,dev_no,dig=dig,ai=ai,ao=ao,fd=fd)
		self.DIO_BITS = 8
	
	def DigitalConfig(self,chan,output=True):
		'''
		Configures digital lines for input/output.
		
		Keyword Arguments:
		subd -- subdevice to perform the config on
		chan -- channel to configure
		output -- default configures device to output, using False is for input
		
		'''
		pass
	
	def DIOWrite(self,chan,val):
		'''
		Writes a value to the specified channel on the subdevice.
			
		Keyword Arguments:
		subd -- subdevice to perform the config on
		chan -- channel to configure
		val -- value: 0/1 --> digital high or low
		
		'''
		pass
	
	def AOInteger(self,chan,rng,dac):
		'''
		Writes a single dac value to the board.
		
		Keyword Arguments:
		chan -- Analog Output Channel
		rng -- The Comedi AO range to use
		dac -- The 16-bit digital voltage value to write
		
		'''
		return dac
	
	def AIInteger(self,chan,rng,diff=True):
		'''
		Takes a single reading on the selected channel and returns
		the value.
		
		Keyword Arguments:
		chan -- Analog Input Channel
		rng -- Analog Input Range to use
		
		'''
		val = ra.randint(0,65535)
		return (1,val,)
	
	def AOBlock(self,chan,rng,wvfrm):
		'''
		Does a DAC output on the NI/comedi device.
		
		Keyword arguments:
		wvfrm -- the waveform to output in 16-bit string format
		chan -- the integer channel number to output on
		
		The write_block function outputs the waveform given in the first argument.
		The argument takes a list which contains your waveform. The list must contain
		2-byte integers (a range of numbers 0-65535). Then it converts the data and
		outputs to the DAC channel specified by the second argument at a frequency
		set in the command structure.
		
		'''
		pass
	
	def AIBlock(self,chan,rng,nscans,ignore=10,diff=True):
		'''
		Reads a block of data from the NI/comedi device.
		
		Keyword arguments:
		nscans -- the number of scans (i.e. data collections) to perform
		chan -- the integer channel number to scan
		minval -- the minimum value the channel can read
		maxval -- the maximum value the channel can read
		ignore -- the number of scans to ignore before starting nscans
		
		Requires 4 arguments. The first is the number of points to average
		over. The second is the analog input channel number to sample on.
		Function returns the averaged value of the samples taken. The last
		two values are used so that comedi can determine the range it should
		use when reading the values. In order to get the best accuracy, comedi
		chooses the smalles possible range based on minval and maxval.
		
		''' 
		datAvg = ra.randint(0,65535)
		return datAvg
	
	def PhysicalToInteger(self,subd,chan,rng,val):
		'''
		Takes a value in physical units, matches it to the given
		comedi range for the analog subdevice and converts it to a python
		integer.
		
		Keyword arguments:
		subd -- subdevice of the
		chan -- the channel that the data references
		rng -- the range that the data references
		val -- the value to convert in physical uints
		
		'''
		intval = ra.randint(0,65535)
		return int(intval)
	
	def IntegerToPhysical(self,subd,chan,rng,dac):
		physval = (ra.random()-0.5)*20
		return physval

device0 = DeviceFunctions(0)
device1 = DeviceFunctions(1,ai=False,ao=True)
devices = [device0,device1]